package com.lantosec.hmrp.core.mvc;


import com.lantosec.hmrp.core.kit.Page;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;

import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * 数据操作基础操作类，主要钟对单表的增删除改查
 *
 * @author 杨成宇
 * @version 1.0
 */
@Log4j2
public abstract class BaseService<R extends BaseRepository<T, ID>, T, ID> {
    protected R repository;

    abstract protected void setRepository(R repository);

    protected T getEntity() {
        try {
            ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
            Class clazz = (Class<T>) ptype.getActualTypeArguments()[1];
            return (T) clazz.newInstance();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

//    protected V getVo() {
//        try {
//            ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
//            log.info(ptype.getActualTypeArguments().length);
//            Class clazz = (Class<V>) ptype.getActualTypeArguments()[3];
//            return (V) clazz.newInstance();
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//        }
//        return null;
//    }


    /**
     * 获取对象不为空的属性
     *
     * @param entity 对象
     * @return 将目标源中不为空的字段取出
     */
    protected String[] getNoNullProperties(T entity) {
        //TODO 还需要思考怎么在插入或更新时设置字段的值为NULL
        //获取对象的bean的包装类
        BeanWrapper bean = new BeanWrapperImpl(entity);
        //获取属性（字段）的描述
        PropertyDescriptor[] descriptors = bean.getPropertyDescriptors();
        Set<String> set = new HashSet<>();
        for (PropertyDescriptor descriptor : descriptors) {
            Object value = bean.getPropertyValue(descriptor.getName());
            if (!Objects.equals(value, null)) set.add(descriptor.getName());
        }
        String[] result = new String[set.size()];
        return set.toArray(result);
    }

    /**
     * 按条件按排序分页查询
     *
     * @param pageNum  当前页
     * @param pageSize 每页的数据条数
     * @param entity   可根对象信息进行精确查找
     * @param sort     分页条件
     * @return map集合
     */
    @Transactional(readOnly = true)
    public Page page(int pageNum, int pageSize, T entity, Sort sort) {
        Pageable pageable = sort != null ? PageRequest.of(pageNum - 1, pageSize, sort) : PageRequest.of(pageNum - 1, pageSize);
        if (entity != null) {
            //查询的实例（条件）
            Example<T> example = Example.of(entity);
            return Page.by(repository.findAll(example, pageable));
        } else {
            return Page.by(repository.findAll(pageable));
        }
    }

    /**
     * 按排序查询全部
     *
     * @param pageNum  当前页
     * @param pageSize 每页的数据条数
     * @param sort     分页条件
     * @return map集合
     */
    @Transactional(readOnly = true)
    public Page page(int pageNum, int pageSize, Sort sort) {
        return page(pageNum, pageSize, null, sort);
    }

    /**
     * 按条件分页查询
     *
     * @param pageNum  当前页
     * @param pageSize 每页的数据条数
     * @param entity   可根对象信息进行精确查找
     * @return map集合
     */
    @Transactional(readOnly = true)
    public Page page(int pageNum, int pageSize, T entity) {
        return page(pageNum, pageSize, entity, null);
    }

    /**
     * 分页查询全部
     *
     * @param pageNum  当前页
     * @param pageSize 每页的数据条数
     * @return map集合
     */
    @Transactional(readOnly = true)
    public Page page(int pageNum, int pageSize) {
        return page(pageNum, pageSize, null, null);
    }

    /**
     * 按条件按排序查询全部
     *
     * @param entity 可根对象信息进行精确查找
     * @param sort   排序
     * @return list集合
     */
    @Transactional(readOnly = true)
    public List<T> getAll(T entity, Sort sort) {
        log.info(entity);
        log.info(sort);

        if (entity != null && sort != null) {
            //查询的实例（条件）
            Example<T> example = Example.of(entity);
            return repository.findAll(example, sort);
        } else if (entity != null) {
            //查询的实例（条件）
            Example<T> example = Example.of(entity);
            return repository.findAll(example);
        } else if (sort != null) {
            return repository.findAll(sort);
        } else {
            return repository.findAll();
        }
    }

    /**
     * 按条件查询
     *
     * @param entity 可根对象信息进行精确查找
     * @return list集合
     */
    @Transactional(readOnly = true)
    public List<T> getAll(T entity) {
        return getAll(entity, null);
    }

    /**
     * 按排序查询全部
     *
     * @param sort 排序
     * @return list集合
     */
    @Transactional(readOnly = true)
    public List<T> getAll(Sort sort) {
        return getAll(null, sort);
    }

    /**
     * 按排序查询全部
     *
     * @return list集合
     */
    @Transactional(readOnly = true)
    public List<T> getAll() {
        return getAll(null, null);
    }

    /**
     * 单条件根据主键查询，未查找到会抛出异常
     *
     * @param key id
     * @return domain
     */
    @Transactional(readOnly = true)
    public T getByKey(ID key) {
        return repository.getOne(key);
    }

    /**
     * 单条件根据主键查询，未查找到不会抛出异常
     *
     * @param id id
     * @return Optional
     */
    @Transactional(readOnly = true)
    public T getById(ID id) {
        Optional<T> optional = repository.findById(id);
        return optional.isPresent() ? optional.get() : null;
    }

    /**
     * 保存
     *
     * @param entity 对象
     * @return domain
     */
    @Transactional
    public T save(T entity) {
        return repository.save(entity);
    }
//
//    /**
//     * po转vo对象
//     *
//     * @param entity
//     * @return
//     */
//    public V getVoFromPo(T entity) {
//        V vo = getVo();
//        BeanUtils.copyProperties(entity, vo);
//        return vo;
//    }
//
//    public List<V> getVoListFromPoList(List<T> list) {
//        List<V> ret = new ArrayList<>();
//        if (list != null && list.size() > 0) {
//            for (T t : list) {
//                ret.add(getVoFromPo(t));
//            }
//        }
//        return ret;
//    }

    /**
     * 批量新增
     *
     * @param entity
     * @return
     */
    @Transactional
    public List<T> saveBatch(List<T> entity) {
        return repository.saveAll(entity);
    }

    /**
     * 删除对象
     *
     * @param entity 对象
     */
    @Transactional
    public void delete(T entity) {
        repository.delete(entity);
    }

    /**
     * 根据id删除
     *
     * @param key id
     */
    @Transactional
    public void deleteById(ID key) {
        repository.deleteById(key);
    }

    /**
     * 批量删除根据id
     *
     * @param ids ids
     */
    @Transactional
    public void deleteBatch(ID[] ids) {
        List<T> list = new ArrayList<>();
        for (ID id : ids) {
            T entity = repository.getOne(id);
            list.add(entity);
        }
        repository.deleteInBatch(list);
    }

    /**
     * 批量删除对象
     *
     * @param entities entities
     */
    @Transactional
    public void deleteBatch(List<T> entities) {
        repository.deleteInBatch(entities);
    }

    /**
     * 批量删除所有
     */
    @Transactional
    public void deleteAllInBatch() {
        repository.deleteAllInBatch();
    }

    /**
     * 更新
     *
     * @param entity 更新参数
     * @param id 需要根据id过滤null值
     * @return 更新的结果
     */
    @Transactional
    public T update(T entity, ID id) {
        T object = repository.getOne(id);
        //将object复制给entity 忽略的属性是有值的属性（将object复制到entity属性为null）
        BeanUtils.copyProperties(object, entity, getNoNullProperties(entity));
        repository.save(entity);
        return entity;
    }

    /**
     * 批量更新
     *
     * @param entities entities
     * @param ids
     */
    @Transactional
    public void updateBatch(List<T> entities, ID[] ids) {
        List<T> list = new ArrayList<>();
        for (T entity : entities) {
            for (ID id : ids) {
                T object = repository.getOne(id);
                BeanUtils.copyProperties(object, entity, getNoNullProperties(entity));
                list.add(entity);
            }
        }
        repository.saveAll(list);
    }
}
